
#include <cassert>
#include <Ws2tcpip.h>

#include "raw_socket.h"
#include "sockexcept.h"
#include "wsaroutine.h"
#include "addr_resolver.h"
#include "raw_socket_impl.h"
using namespace sock;



void RawSocket::write( const void *buf, int len )
{
	assert( _pimpl->sock != INVALID_SOCKET );

	if( len > 0 )
	{
		int bytes_sent = ::send( _pimpl->sock, (char*) buf, len, 0 );
		if( bytes_sent != len ){
			_pimpl->sock = INVALID_SOCKET;
			_pimpl->connected_flag = false;
			throw SocketError( WSAGetLastError() );
		}
	}
}

int RawSocket::read( void *buf, int len )
{
	assert( len != 0 );
	assert( _pimpl->sock != INVALID_SOCKET );

	int bytes_recv = ::recv( _pimpl->sock, (char*) buf, len, 0 );
	if( bytes_recv == SOCKET_ERROR ){
		_pimpl->sock = INVALID_SOCKET;
		_pimpl->connected_flag = false;
		throw SocketError( WSAGetLastError() );
	}
	return bytes_recv;
}


void RawSocket::connect( const InetAddr & addr )
{
	if( _pimpl->sock == INVALID_SOCKET )
	{
		_pimpl->create();
	}

	AddrResolver ar( addr );
	size_t i = 0;
	int res = SOCKET_ERROR;
	while( i < ar.size() && res == SOCKET_ERROR ){
		res = ::connect( _pimpl->sock, ar[i]->ai_addr, (int) ar[i]->ai_addrlen );
		i++;
	}
	if( res == SOCKET_ERROR ){
		throw SocketError( WSAGetLastError() );
	}
	_pimpl->connected_flag = true;
}

RawSocket::RawSocket():  _pimpl( new Impl ), _pcounter( new unsigned(1) )
{
	WsaRoutine & wsaroutine = WsaRoutine::instance();

	_pimpl->create();
}

RawSocket::RawSocket( int sock ): _pimpl( new Impl ), _pcounter( new unsigned(1) )
{
	assert( sock != INVALID_SOCKET );

	WsaRoutine & wsaroutine = WsaRoutine::instance();
	_pimpl->sock = sock;
}

RawSocket::RawSocket(const sock::RawSocket &other)
{
	_pimpl = other._pimpl;
	_pcounter = other._pcounter;
	++*_pcounter;
}

RawSocket & RawSocket::operator =(const sock::RawSocket &other)
{
	RawSocket tmp( other );
	swap( tmp );
	return *this;
}

void RawSocket::swap( RawSocket &other )
{
	std::swap( _pimpl, other._pimpl );
	std::swap( _pcounter, other._pcounter );
}


RawSocket::~RawSocket()
{
	assert( _pimpl->sock != INVALID_SOCKET );
	assert( _pcounter != NULL );
	assert( *_pcounter != 0 );

	if( *_pcounter == 1 ){
		::closesocket( _pimpl->sock );
		delete _pcounter;
		delete _pimpl;
	} else {
		--*_pcounter;
	}
}

void RawSocket::bind( const InetAddr & addr )
{
	assert( _pimpl->sock != INVALID_SOCKET );

	AddrResolver ar( addr );
	size_t i = 0;
	int res = SOCKET_ERROR;
	while( i < ar.size() && res == SOCKET_ERROR ){
		res = ::bind( _pimpl->sock, ar[i]->ai_addr, (int) ar[i]->ai_addrlen );
	}
	if( res == SOCKET_ERROR ){
		throw SocketError( WSAGetLastError() );
	}
}

void RawSocket::listen()
{
	listen( SOMAXCONN );
}

void RawSocket::listen( int maxconn )
{
	int res = ::listen( _pimpl->sock, maxconn );
	if ( res == SOCKET_ERROR ){
		throw SocketError( WSAGetLastError() );
	}
}

RawSocket RawSocket::accept()
{
	SOCKET newsock = ::accept( _pimpl->sock, NULL, 0 );
	return RawSocket( (int) newsock );
}

bool RawSocket::isconnected() const
{
	return _pimpl->connected_flag;
}
